﻿using System.Collections.Generic;

namespace Zove.Runtime.Core.InferencingStrategy {

    /// <summary>
    /// Network algorithm
    /// </summary>
    public class NetworkingInferencingStrategy : BasicInferencingStrategy {

        /// <summary>
        /// Default constructor
        /// </summary>
        public NetworkingInferencingStrategy() {
            Name = "Network Inferencing Strategy";
        }

        /// <summary>
        /// Run the algorithm
        /// </summary>
        /// <param name="zoveObject"></param>
        public override void Infer(ZoveObject zoveObject) {
            bool updated = false;
            List<RuntimeFactor> runningChain = new List<RuntimeFactor>(),
                                newChain = new List<RuntimeFactor>();
            RuntimeFactor target = null;

            foreach (RuntimeFactor prf in zoveObject.Model.Factors) {
                if (prf.Status == RuntimeFactorStatus.Input) {
                    runningChain.Add(prf);
                }
            }
            //runningChain = from f in zoveObject.Model.Factors where f.Status == RuntimeFactorStatus.Input select f;

            do {
                updated = false;
                foreach (RuntimeFactor rf in runningChain) {
                    foreach (RuntimeEquation re in rf.Equations) {
                        int unknownFactorCount = 0;
                        foreach (RuntimeFactor rff in re.Factors) {
                            if (rff.Status == RuntimeFactorStatus.Unknown) {
                                unknownFactorCount++;
                                if (unknownFactorCount > 1) {
                                    break;
                                }

                                target = rff;
                            }
                        }
                        if (unknownFactorCount == 1) {
                            if (!runningChain.Contains(target)) {
                                RuntimeRule rr = zoveObject.Model.GetRuntimeRule(re, target);
                                if (rr.Root == null) {
                                    continue;
                                }

                                updated = true;

                                newChain.Add(target);
                                target.Solve(rr);

                                var se = new SolvedFactor();
                                se.Strategy = this;
                                se.Factor = target;
                                se.Rule = rr;

                                //base.Evaluate(se);
                                zoveObject.Queue[target.Name] = se;
                            }
                        }
                    }
                }

                runningChain.AddRange(newChain);
                newChain.Clear();
            } while (updated);
        }
    }
}